Vue 响应式原理解析
大家都知道 vue 是双向数据绑定,vue 的双向数据绑定使用的是 v-model 实现。v-model实际上是 input 的事件 和 value 属性值。其最本质的原理是基于 vue 的响应式原理实现。响应式的实现原理是怎么样的呢?让我们接下来一起看看
1. 数据劫持
源码位置:vue/src/core/observer/index.ts
在vue 源码有这么一个函数 defineReactive,其本质是使用Object.defineProperty 方法劫持对象属性,使得在访问和修改属性时能够执行特定的操作
- get: 用于获取属性值,同时进行依赖收集
- set: 设置新值,触发依赖更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| export function defineReactive( obj: object, key: string, val?: any, customSetter?: Function | null, shallow?: boolean, mock?: boolean ) { const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key) if (property && property.configurable === false) { return }
const getter = property && property.get const setter = property && property.set if ( (!getter || setter) && (val === NO_INITIAL_VALUE || arguments.length === 2) ) { val = obj[key] }
let childOb = !shallow && observe(val, false, mock) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val if (Dep.target) { if (__DEV__) { dep.depend({ target: obj, type: TrackOpTypes.GET, key }) } else { dep.depend() } if (childOb) { childOb.dep.depend() if (isArray(value)) { dependArray(value) } } } return isRef(value) && !shallow ? value.value : value }, set: function reactiveSetter(newVal) { const value = getter ? getter.call(obj) : val if (!hasChanged(value, newVal)) { return } if (__DEV__ && customSetter) { customSetter() } if (setter) { setter.call(obj, newVal) } else if (getter) { return } else if (!shallow && isRef(value) && !isRef(newVal)) { value.value = newVal return } else { val = newVal } childOb = !shallow && observe(newVal, false, mock) if (__DEV__) { dep.notify({ type: TriggerOpTypes.SET, target: obj, key, newValue: newVal, oldValue: value }) } else { dep.notify() } } })
return dep }
|
其中 observe函数,递归遍历对象属性,为每个属性创建一个dep
实例,通过Object.defineProperty 为每个属性设置 getter 和 setter。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function observe(obj) { if (typeof obj !== 'object' || obj === null) { return }
Object.keys(obj).forEach(key => { let internalValue = obj[key]
const dep = new Dep(); Object.defineProperty(obj, key, { get() { dep.depend() return internalValue }, set(newValue) { if (newValue !== internalValue) { internalValue = newValue dep.notify() } } }) observe(internalValue) }) }
|
2. 依赖追踪
vue 的响应式系统引入了一个重要概念 – 依赖追踪。每个响应式属性都会关联一个 Dep
(依赖)对象,它复制收集所有依赖于该属性的观察者(Watcher)。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Dep { constructor() { this.subscribers = new Set(); }
depend() { if (activeWatcher) { this.subscribers.add(activeWatcher); } }
notify() { this.subscribers.forEach(subscribe => { subscribe.update(); }) } }
|
当属性被访问时,会调用 dep.depend()
,将当前的观察者添加到依赖集合中。当属性被修改时,会调用 dep.notify()
,通知所有依赖的观察者进行更新。
3.观察者模式
观察者模式是vue 响应式系统的核心。通过创建观察者对象,订阅并响应数据的变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Watcher { constructor(updateFn) { this.updateFn = updateFn }
update() { this.updateFn() }
run() { activeWatcher = this this.update() activeWatcher = null } }
|
总结:
Vue 采用 数据劫持 结合 发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty 来劫持数据的 setter 和 getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。
参考链接:
https://tsejx.github.io/vue-guidebook/infrastructure/vue2/reactivity/#%E6%80%BB%E7%BB%93